package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
)

var (
	date        = flag.String("date", "", "指定日期范围，格式为YYYY-MM或YYYY-MM-DD")
	format      = flag.String("f", "raw", "输出格式 (raw: 原始链接, url: 仅URL部分, decoded: 解码后的链接)")
	noClipboard = flag.Bool("no-clipboard", false, "不复制到剪贴板")
	keepFile    = flag.Bool("keep-file", false, "保留下载的Markdown文件，不自动删除")
)

func getCurrentDate() (year, month, day int) {
	now := time.Now()
	return now.Year(), int(now.Month()), now.Day()
}

func generateFileNames(year, month, day int) []string {
	var fileNames []string
	yearMonth := fmt.Sprintf("%d-%02d", year, month)

	if day == 0 {
		// 生成该月所有可能的日期
		for d := 1; d <= 31; d++ {
			for h := 0; h < 24; h++ {
				for m := 0; m < 60; m += 30 {
					filename := fmt.Sprintf("%s/%02d日%02d时%02d分.md", yearMonth, d, h, m)
					fileNames = append(fileNames, filename)
				}
			}
		}
	} else {
		// 生成当天的文件
		for h := 0; h < 24; h++ {
			for m := 0; m < 60; m += 30 {
				filename := fmt.Sprintf("%s/%02d日%02d时%02d分.md", yearMonth, day, h, m)
				fileNames = append(fileNames, filename)
			}
		}
	}
	return fileNames
}

func findLatestFile(fileNames []string) (string, string) {
	baseURL := "https://raw.githubusercontent.com/sharkDoor/vpn-free-nodes/master/node-list/"
	sort.Sort(sort.Reverse(sort.StringSlice(fileNames)))

	fmt.Printf("开始查找最新文件，共%d个候选...\n", len(fileNames))
	for i, fileName := range fileNames {
		url := baseURL + fileName
		fmt.Printf("尝试请求 [%d/%d]: %s\n", i+1, len(fileNames), fileName)

		client := &http.Client{Timeout: 10 * time.Second}
		resp, err := client.Get(url)
		if err != nil {
			fmt.Printf("⚠️ 请求出错: %v - %s\n", err, fileName)
			continue
		}
		defer resp.Body.Close()

		if resp.StatusCode == 200 {
			fmt.Printf("✅ 找到有效文件: %s\n", fileName)
			return url, fileName
		} else {
			fmt.Printf("❌ 文件不存在 (状态码: %d): %s\n", resp.StatusCode, fileName)
		}

		time.Sleep(500 * time.Millisecond) // 避免请求过于频繁
	}

	fmt.Println("❌ 未找到有效文件")
	return "", ""
}

func downloadFile(url, fileName string) string {
	localFileName := strings.Replace(fileName, "/", "_", -1)
	fmt.Printf("开始下载文件: %s\n", localFileName)

	client := &http.Client{Timeout: 15 * time.Second}
	resp, err := client.Get(url)
	if err != nil {
		fmt.Printf("⚠️ 下载过程发生错误: %v\n", err)
		return ""
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		fmt.Printf("❌ 下载失败，状态码: %d\n", resp.StatusCode)
		return ""
	}

	file, err := os.Create(localFileName)
	if err != nil {
		fmt.Printf("⚠️ 创建文件失败: %v\n", err)
		return ""
	}
	defer file.Close()

	_, err = io.Copy(file, resp.Body)
	if err != nil {
		fmt.Printf("⚠️ 写入文件失败: %v\n", err)
		return ""
	}

	fmt.Printf("✅ 下载完成: %s\n", localFileName)
	return localFileName
}

func extractLinksFromMD(filePath string) []string {
	fmt.Printf("开始从 %s 中提取链接...\n", filePath)
	data, err := os.ReadFile(filePath)
	if err != nil {
		fmt.Printf("⚠️ 读取文件失败: %v\n", err)
		return nil
	}

	content := string(data)

	// 更精确的匹配模式
	patterns := []struct {
		Name    string
		Pattern string
	}{
		{"基本模式", `\btrojan://[^|\s\n]+`},
		{"表格模式", `\|[^\|]*\btrojan://[^|\s\n]+[^\|]*\|`},
		{"HTML链接模式", `href\s*=\s*['"]?trojan://[^'"\s>]+`},
		{"编码模式", `\btrojan%3A%2F%2F[^|\s\n]+`},
	}

	var links []string

	// 尝试每种模式
	for _, p := range patterns {
		pattern := regexp.MustCompile(p.Pattern)
		matches := pattern.FindAllString(content, -1)

		if len(matches) > 0 {
			fmt.Printf("使用模式 '%s' 找到 %d 个匹配\n", p.Name, len(matches))

			for _, match := range matches {
				// 清理匹配结果
				cleanLink := cleanTrojanLink(match)
				if cleanLink != "" {
					links = append(links, cleanLink)
				}
			}
		}
	}

	if len(links) == 0 {
		// 输出文件前100个字符用于调试
		preview := content
		if len(preview) > 100 {
			preview = preview[:100] + "..."
		}
		fmt.Printf("⚠️ 未找到Trojan链接。文件预览: %q\n", preview)
		return nil
	}

	// 去重
	uniqueLinks := make(map[string]struct{})
	var result []string
	for _, link := range links {
		if _, exists := uniqueLinks[link]; !exists {
			uniqueLinks[link] = struct{}{}
			result = append(result, link)
		}
	}

	fmt.Printf("成功提取 %d 个唯一链接\n", len(result))
	return result
}

// 清理提取的链接，去除可能的前缀和后缀
func cleanTrojanLink(rawLink string) string {
	// 去除表格边界
	rawLink = strings.TrimPrefix(rawLink, "|")
	rawLink = strings.TrimSuffix(rawLink, "|")

	// 去除HTML标签前缀
	rawLink = strings.TrimPrefix(rawLink, "href=")
	rawLink = strings.TrimPrefix(rawLink, `"`)
	rawLink = strings.TrimPrefix(rawLink, `'`)

	// 去除HTML标签后缀
	rawLink = strings.TrimSuffix(rawLink, `"`)
	rawLink = strings.TrimSuffix(rawLink, `'`)
	rawLink = strings.TrimSuffix(rawLink, `>`)

	// 去除多余的空格
	rawLink = strings.TrimSpace(rawLink)

	// 确保以trojan://开头
	if !strings.HasPrefix(rawLink, "trojan://") {
		return ""
	}

	return rawLink
}

func printLinks(links []string, format string) string {
	if len(links) == 0 {
		fmt.Println("未找到有效链接")
		return ""
	}

	var result []string
	switch format {
	case "raw":
		result = links
	case "url":
		for _, link := range links {
			result = append(result, strings.Replace(link, "trojan://", "", 1))
		}
	case "decoded":
		for _, link := range links {
			urlPart := strings.Replace(link, "trojan://", "", 1)
			decoded, err := url.QueryUnescape(urlPart)
			if err != nil {
				decoded = urlPart // 解码失败则使用原始值
			}
			result = append(result, "trojan://"+decoded)
		}
	}

	// 打印带序号的链接列表
	for i, link := range result {
		fmt.Printf("%d. %s\n", i+1, link)
	}

	// 返回不带序号的文本（用于剪贴板）
	return strings.Join(result, "\n")
}

func copyToClipboard(text string) {
	if text == "" {
		return
	}

	fmt.Println("尝试复制链接到剪贴板...")

	// 不同平台的剪贴板实现
	switch runtime.GOOS {
	case "windows":
		// Windows平台实现
		cmd := exec.Command("powershell", "-Command",
			"Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::SetText('"+
				strings.Replace(text, "'", "`'", -1)+"')")

		var out bytes.Buffer
		cmd.Stderr = &out

		if err := cmd.Run(); err != nil {
			fmt.Printf("⚠️ 剪贴板操作失败: %v - %s\n", err, out.String())
			// 备用方法
			cmd = exec.Command("clip")
			cmd.Stdin = strings.NewReader(text)
			if err := cmd.Run(); err != nil {
				fmt.Printf("⚠️ 备用剪贴板方法也失败: %v\n", err)
			} else {
				fmt.Println("✅ 链接已复制到剪贴板")
			}
		} else {
			fmt.Println("✅ 链接已复制到剪贴板")
		}

	case "darwin":
		// macOS平台实现
		cmd := exec.Command("pbcopy")
		cmd.Stdin = strings.NewReader(text)

		if err := cmd.Run(); err != nil {
			fmt.Printf("⚠️ 剪贴板操作失败: %v\n", err)
		} else {
			fmt.Println("✅ 链接已复制到剪贴板")
		}

	case "linux":
		// Linux平台实现
		// 检查xclip是否可用
		hasXclip := false
		if _, err := exec.LookPath("xclip"); err == nil {
			hasXclip = true
		}

		// 检查xsel是否可用
		hasXsel := false
		if _, err := exec.LookPath("xsel"); err == nil {
			hasXsel = true
		}

		if hasXclip || hasXsel {
			fmt.Println("使用xclip或xsel复制到剪贴板")
		} else {
			fmt.Println("⚠️ 无法找到剪贴板工具(xclip/xsel)，请手动复制")
			return
		}

		var cmd *exec.Cmd
		if hasXclip {
			cmd = exec.Command("xclip", "-selection", "clipboard")
		} else if hasXsel {
			cmd = exec.Command("xsel", "--clipboard", "--input")
		}

		cmd.Stdin = strings.NewReader(text)

		if err := cmd.Run(); err != nil {
			fmt.Printf("⚠️ 剪贴板操作失败: %v\n", err)
		} else {
			fmt.Println("✅ 链接已复制到剪贴板")
		}

	default:
		fmt.Println("⚠️ 不支持的操作系统，无法复制到剪贴板")
	}
}

// deleteFile 函数：删除指定的文件并处理可能的错误
func deleteFile(filePath string) {
	if filePath == "" {
		return
	}

	// 检查文件是否存在
	_, err := os.Stat(filePath)
	if os.IsNotExist(err) {
		return // 文件已不存在，无需删除
	}

	err = os.Remove(filePath)
	if err != nil {
		fmt.Printf("⚠️ 无法删除文件 %s: %v\n", filePath, err)
		return
	}

	fmt.Printf("🗑️ 已删除临时文件: %s\n", filePath)
}

func main() {
	flag.Parse()

	// 1. 解析日期参数
	var year, month, day int
	if *date != "" {
		parts := strings.Split(*date, "-")
		if len(parts) == 2 {
			y, err := strconv.Atoi(parts[0])
			if err != nil {
				fmt.Println("日期格式错误，请使用YYYY-MM或YYYY-MM-DD格式")
				return
			}
			m, err := strconv.Atoi(parts[1])
			if err != nil || m < 1 || m > 12 {
				fmt.Println("日期格式错误，请使用YYYY-MM或YYYY-MM-DD格式")
				return
			}
			year, month, day = y, m, 0
			fmt.Printf("已指定日期范围: %d年%02d月\n", year, month)
		} else if len(parts) == 3 {
			y, err := strconv.Atoi(parts[0])
			if err != nil {
				fmt.Println("日期格式错误，请使用YYYY-MM或YYYY-MM-DD格式")
				return
			}
			m, err := strconv.Atoi(parts[1])
			if err != nil || m < 1 || m > 12 {
				fmt.Println("日期格式错误，请使用YYYY-MM或YYYY-MM-DD格式")
				return
			}
			d, err := strconv.Atoi(parts[2])
			if err != nil || d < 1 || d > 31 {
				fmt.Println("日期格式错误，请使用YYYY-MM或YYYY-MM-DD格式")
				return
			}
			year, month, day = y, m, d
			fmt.Printf("已指定完整日期: %d年%02d月%02d日\n", year, month, day)
		} else {
			fmt.Println("日期格式错误，请使用YYYY-MM或YYYY-MM-DD格式")
			return
		}
	} else {
		// 未提供参数，使用系统当前日期
		year, month, day = getCurrentDate()
		fmt.Printf("未指定日期，使用系统当前日期: %d年%02d月%02d日\n", year, month, day)
	}

	// 2. 生成候选文件列表并查找最新文件
	fileNames := generateFileNames(year, month, day)
	url, fileName := findLatestFile(fileNames)
	if url == "" || fileName == "" {
		fmt.Println("下载失败，无法继续")
		return
	}

	// 3. 下载文件
	downloadedFile := downloadFile(url, fileName)
	if downloadedFile == "" {
		fmt.Println("下载文件失败，无法继续")
		return
	}

	// 4. 提取链接
	links := extractLinksFromMD(downloadedFile)
	if links == nil || len(links) == 0 {
		fmt.Println("未提取到有效链接")

		// 即使提取失败，也删除文件
		deleteFile(downloadedFile)
		return
	}

	fmt.Printf("✅ 成功提取 %d 个链接\n", len(links))

	// 5. 输出并复制到剪贴板
	linkText := printLinks(links, *format)
	if !*noClipboard {
		copyToClipboard(linkText)
	}

	// 6. 自动删除下载的Markdown文件（除非指定保留）
	if !*keepFile {
		deleteFile(downloadedFile)
	}
}
